#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/time.h>


#include "system_info.h"

#define RATE_INTERVAL_TIME 2

#define NCPUSTATES 5
static char *cpustatenames[NCPUSTATES+1] =
{
    "user", "nice", "system", "idle", "iowait",
    NULL
};

#define MEMUSED    0
#define MEMFREE    1
#define MEMSHARED  2
#define MEMBUFFERS 3
#define MEMCACHED  4
#define NMEMSTATS  5

static char *memorynames[NMEMSTATS+1] =
{
    "K used, ", "K free, ", "K shared, ", "K buffers, ", "K cached",
    NULL
};

#define SWAPUSED   0
#define SWAPFREE   1
#define SWAPCACHED 2
#define NSWAPSTATS 3
static char *swapnames[NSWAPSTATS+1] =
{
    "K used, ", "K free, ", "K cached",
    NULL
};

/* useful macros */
#define bytetok(x)	(((x) + 512) >> 10)
#define pagetok(x)	((x) << (PAGE_SHIFT - 10))
#define HASH(x)		(((x) * 1686629713U) % HASH_SIZE)


static int show_iowait = 0;


/* these are for calculating cpu state percentages */

static long cp_time[NCPUSTATES];
static long cp_old[NCPUSTATES];
static long cp_diff[NCPUSTATES];


/* for calculating the exponential average */

static struct timeval lasttime = { 0, 0 };
static struct timeval timediff = { 0, 0 };
static long elapsed_msecs;


/* these are for passing data back to the machine independant portion */

static int cpu_states[NCPUSTATES];
//static int process_states[NPROCSTATES];
//static int kernel_stats[NKERNELSTATS];
static long memory_stats[NMEMSTATS];
static long swap_stats[NSWAPSTATS];

static inline char *
skip_token(const char *p)
{
    while (isspace(*p)) p++;
    while (*p && !isspace(*p)) p++;
    return (char *)p;
}

static inline char *
skip_ws(const char *p)
{
    while (isspace(*p)) p++;
    return (char *)p;
}

/*
 *  percentages(cnt, out, new, old, diffs) - calculate percentage change
 *	between array "old" and "new", putting the percentages i "out".
 *	"cnt" is size of each array and "diffs" is used for scratch space.
 *	The array "old" is updated on each call.
 *	The routine assumes modulo arithmetic.  This function is especially
 *	useful on BSD mchines for calculating cpu state percentages.
 */

long
percentages(int cnt, int *out, long *new, long *old, long *diffs)

{
    register int i;
    register long change;
    register long total_change;
    register long *dp;
    long half_total;

    /* initialization */
    total_change = 0;
    dp = diffs;

    /* calculate changes for each state and the overall change */
    for (i = 0; i < cnt; i++)
    {
	if ((change = *new - *old) < 0)
	{
	    /* this only happens when the counter wraps */
	    change = (int)
		((unsigned long)*new-(unsigned long)*old);
	}
	total_change += (*dp++ = change);
	*old++ = *new++;
    }

    /* avoid divide by zero potential */
    if (total_change == 0)
    {
	total_change = 1;
    }

    /* calculate percentages based on overall change, rounding up */
    half_total = total_change / 2l;
    for (i = 0; i < cnt; i++)
    {
	*out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
    }

    /* return the total in case the caller wants to use it */
    return(total_change);
}

static void
get_system_info(struct system_info *info)
{
    char buffer[4096+1];
	char stat_path[] = "/proc/stat";
	char meminfo_path[] = "/proc/meminfo";
    int fd, len;
    char *p;
    struct timeval thistime;
    unsigned long intr = 0;
    unsigned long ctxt = 0;
    unsigned long newproc = 0;

    /* timestamp and time difference */
    gettimeofday(&thistime, 0);
    timersub(&thistime, &lasttime, &timediff);
    elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000;
    lasttime = thistime;

    /* get the cpu time info */
    if ((fd = open(stat_path, O_RDONLY)) != -1)
    {
	if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
	{
	    buffer[len] = '\0';
	    p = skip_token(buffer);			/* "cpu" */
	    cp_time[0] = strtoul(p, &p, 0);
	    cp_time[1] = strtoul(p, &p, 0);
	    cp_time[2] = strtoul(p, &p, 0);
	    cp_time[3] = strtoul(p, &p, 0);
	    if (show_iowait)
	    {
		cp_time[4] = strtoul(p, &p, 0);
	    }

	    /* convert cp_time counts to percentages */
	    percentages(NCPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
	}
	close(fd);
    }else{
		printf("ERROR: Open file %s\n",stat_path);
    }

    /* get system wide memory usage */
    if ((fd = open(meminfo_path, O_RDONLY)) != -1)
    {
	char *p;
	int mem = 0;
	int swap = 0;
	unsigned long memtotal = 0;
	unsigned long memfree = 0;
	unsigned long swaptotal = 0;

	if ((len = read(fd, buffer, sizeof(buffer)-1)) > 0)
	{
	    buffer[len] = '\0';
	    p = buffer-1;

	    /* iterate thru the lines */
	    while (p != NULL)
		{
			p++;
			if (p[0] == ' ' || p[0] == '\t')
			{
			/* skip */
			}
			else if (strncmp(p, "Mem:", 4) == 0)
			{
				p = skip_token(p);			/* "Mem:" */
				p = skip_token(p);			/* total memory */
				memory_stats[MEMUSED] = strtoul(p, &p, 10);
				memory_stats[MEMFREE] = strtoul(p, &p, 10);
				memory_stats[MEMSHARED] = strtoul(p, &p, 10);
				memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
				memory_stats[MEMCACHED] = strtoul(p, &p, 10);
				memory_stats[MEMUSED] = bytetok(memory_stats[MEMUSED]);
				memory_stats[MEMFREE] = bytetok(memory_stats[MEMFREE]);
				memory_stats[MEMSHARED] = bytetok(memory_stats[MEMSHARED]);
				memory_stats[MEMBUFFERS] = bytetok(memory_stats[MEMBUFFERS]);
				memory_stats[MEMCACHED] = bytetok(memory_stats[MEMCACHED]);
				mem = 1;
			}
			else if (strncmp(p, "Swap:", 5) == 0)
			{
				p = skip_token(p);			/* "Swap:" */
				p = skip_token(p);			/* total swap */
				swap_stats[SWAPUSED] = strtoul(p, &p, 10);
				swap_stats[SWAPFREE] = strtoul(p, &p, 10);
				swap_stats[SWAPUSED] = bytetok(swap_stats[SWAPUSED]);
				swap_stats[SWAPFREE] = bytetok(swap_stats[SWAPFREE]);
				swap = 1;
			}
			else if (!mem && strncmp(p, "MemTotal:", 9) == 0)
			{
				p = skip_token(p);
				memtotal = strtoul(p, &p, 10);
			}
			else if (!mem && memtotal > 0 && strncmp(p, "MemFree:", 8) == 0)
			{
				p = skip_token(p);
				memfree = strtoul(p, &p, 10);
				memory_stats[MEMUSED] = memtotal - memfree;
				memory_stats[MEMFREE] = memfree;
			}
			else if (!mem && strncmp(p, "MemShared:", 10) == 0)
			{
				p = skip_token(p);
				memory_stats[MEMSHARED] = strtoul(p, &p, 10);
			}
			else if (!mem && strncmp(p, "Buffers:", 8) == 0)
			{
				p = skip_token(p);
				memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
			}
			else if (!mem && strncmp(p, "Cached:", 7) == 0)
			{
				p = skip_token(p);
				memory_stats[MEMCACHED] = strtoul(p, &p, 10);
			}
			else if (!swap && strncmp(p, "SwapTotal:", 10) == 0)
			{
				p = skip_token(p);
				swaptotal = strtoul(p, &p, 10);
			}
			else if (!swap && swaptotal > 0 && strncmp(p, "SwapFree:", 9) == 0)
			{
				p = skip_token(p);
				memfree = strtoul(p, &p, 10);
				swap_stats[SWAPUSED] = swaptotal - memfree;
				swap_stats[SWAPFREE] = memfree;
			}
			else if (!mem && strncmp(p, "SwapCached:", 11) == 0)
			{
				p = skip_token(p);
				swap_stats[SWAPCACHED] = strtoul(p, &p, 10);
			}

			/* move to the next line */
			p = strchr(p, '\n');
	    }
	}
	close(fd);
    }else{
		printf("ERROR: Open file %s\n",meminfo_path);
    }

    /* set arrays and strings */
    info->cpustates = cpu_states;
    info->memory = memory_stats;
    info->swap = swap_stats;
//    info->kernel = kernel_stats;
}
/*
 *Get real CPU rate
 */
float get_user_cpu_rate()
{
	struct system_info info;
	get_system_info(&info);
	sleep(RATE_INTERVAL_TIME);
	get_system_info(&info);
	return (float)info.cpustates[0]/10;
}

float get_mem_rate()
{
	struct system_info info;
	get_system_info(&info);
	sleep(RATE_INTERVAL_TIME);
	get_system_info(&info);
	return 	(double)info.memory[MEMUSED]*100/
			(double)(info.memory[MEMFREE]+info.memory[MEMUSED]);
}

long get_total_mem_size()
{
	struct system_info info;
	get_system_info(&info);
	return (info.memory[MEMFREE]+info.memory[MEMUSED]);
}
